﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LoA.Shared.Input;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace LoA.InputEngine {
	public partial class InputComponent {

		SpriteBatch spriteBatch;
		SpriteFont spriteFont;

		// Direction textures.
		Texture2D upTexture;
		Texture2D downTexture;
		Texture2D leftTexture;
		Texture2D rightTexture;
		Texture2D upLeftTexture;
		Texture2D upRightTexture;
		Texture2D downLeftTexture;
		Texture2D downRightTexture;
		Texture2D turnLeftTexture;
		Texture2D turnRightTexture;

		// Button textures.
		Texture2D aButtonTexture;
		Texture2D bButtonTexture;
		Texture2D xButtonTexture;
		Texture2D yButtonTexture;

		// Other textures.
		Texture2D plusTexture;
		Texture2D padFaceTexture;

		protected override void LoadContent( ) {
			// Create a new SpriteBatch, which can be used to draw textures.
			spriteBatch = new SpriteBatch(GraphicsDevice);

			spriteFont = Game.Content.Load<SpriteFont>("Font/FunyFont");

			// Load direction textures.
			upTexture = Game.Content.Load<Texture2D>("Texture/Button/Up");
			downTexture = Game.Content.Load<Texture2D>("Texture/Button/Down");
			leftTexture = Game.Content.Load<Texture2D>("Texture/Button/Left");
			rightTexture = Game.Content.Load<Texture2D>("Texture/Button/Right");
			upLeftTexture = Game.Content.Load<Texture2D>("Texture/Button/UpLeft");
			upRightTexture = Game.Content.Load<Texture2D>("Texture/Button/UpRight");
			downLeftTexture = Game.Content.Load<Texture2D>("Texture/Button/DownLeft");
			downRightTexture = Game.Content.Load<Texture2D>("Texture/Button/DownRight");
			turnLeftTexture = Game.Content.Load<Texture2D>("Texture/Button/TurnLeft");
			turnRightTexture = Game.Content.Load<Texture2D>("Texture/Button/TurnRight");

			// Load button textures.
			aButtonTexture = Game.Content.Load<Texture2D>("Texture/Button/A");
			bButtonTexture = Game.Content.Load<Texture2D>("Texture/Button/B");
			xButtonTexture = Game.Content.Load<Texture2D>("Texture/Button/X");
			yButtonTexture = Game.Content.Load<Texture2D>("Texture/Button/Y");

			// Load other textures.
			plusTexture = Game.Content.Load<Texture2D>("Texture/Button/Plus");
			padFaceTexture = Game.Content.Load<Texture2D>("Texture/Button/PadFace");

			base.LoadContent( );
		}

		protected override void UnloadContent( ) {
			base.UnloadContent( );
		}

		/// <summary>
		/// This is called when the game should draw itself.
		/// </summary>
		/// <param name="gameTime">Provides a snapshot of timing values.</param>
		public override void Draw(GameTime gameTime) {/*
			spriteBatch.Begin( );

			// Calculate some reasonable boundaries within the safe area.
			Vector2 topLeft = new Vector2(50, 50);
			Vector2 bottomRight = new Vector2(
				GraphicsDevice.Viewport.Width - topLeft.X,
				GraphicsDevice.Viewport.Height - topLeft.Y);

			// Keeps track of where to draw next.
			Vector2 position = topLeft;

			// Draw the list of all moves.
			foreach (Move move in moves) {
				Vector2 size = MeasureMove(move);

				// If this move would fall off the right edge of the screen,
				if (position.X + size.X > bottomRight.X) {
					// start again on the next line.
					position.X = topLeft.X;
					position.Y += size.Y;
				}

				DrawMove(move, position);
				position.X += size.X + 30.0f;
			}

			// Skip some space.
			position.Y += 90.0f;

			// Draw the input from each player.
			for (int i = 0; i < inputManagers.Length; ++i) {
				position.X = topLeft.X;
				DrawInput(i, position);
				position.Y += 80;
			}

			spriteBatch.End( );

			/// Fix GraphicDevice after spriteBatch for 3D Graphics
			GraphicsDevice.BlendState = BlendState.Opaque;
			GraphicsDevice.DepthStencilState = DepthStencilState.Default;
			GraphicsDevice.SamplerStates[0] = SamplerState.LinearWrap;
			*/
			base.Draw(gameTime);
		}

		/// <summary>
		/// Calculates the size of what would be drawn by a call to DrawMove.
		/// </summary>
		private Vector2 MeasureMove(MoveSequence move) {
			Vector2 textSize = spriteFont.MeasureString(move.Name);
			Vector2 sequenceSize = MeasureSequence(move.Sequence);
			return new Vector2(
				Math.Max(textSize.X, sequenceSize.X),
				textSize.Y + sequenceSize.Y);
		}

		/// <summary>
		/// Draws graphical instructions on how to perform a move.
		/// </summary>
		private void DrawMove(MoveSequence move, Vector2 position) {
			DrawString(move.Name, position, Color.White);
			position.Y += spriteFont.MeasureString(move.Name).Y;
			DrawSequence(move.Sequence, position);
		}

		/// <summary>
		/// Draws the input buffer and most recently fired action for a given player.
		/// </summary>
		private void DrawInput(int i, Vector2 position) {
			InputManager inputManager = inputManagers[i];
			MoveSequence move = playerMoves[i];

			// Draw the player's name and currently active move (if any).
			string text = "Player " + inputManager.PlayerIndex + " input  ";
			Vector2 textSize = spriteFont.MeasureString(text);
			DrawString(text, position, Color.White);
			if (move != null) {
				DrawString(move.Name,
					new Vector2(position.X + textSize.X, position.Y), Color.Red);
			}

			// Draw the player's input buffer.
			position.Y += textSize.Y;
			DrawSequence(inputManager.buffer, position);
		}

		/// <summary>
		/// Draws a string with a subtle drop shadow.
		/// </summary>
		private void DrawString(string text, Vector2 position, Color color) {
			spriteBatch.DrawString(spriteFont, text,
				new Vector2(position.X, position.Y + 1), Color.Black);
			spriteBatch.DrawString(spriteFont, text,
				new Vector2(position.X, position.Y), color);
		}

		/// <summary>
		/// Calculates the size of what would be drawn by a call to DrawSequence.
		/// </summary>
		private Vector2 MeasureSequence(IEnumerable<Buttons> sequence) {
			float width = 0.0f;
			foreach (Buttons buttons in sequence) {
				width += MeasureButtons(buttons).X;
			}
			return new Vector2(width, padFaceTexture.Height);
		}

		/// <summary>
		/// Draws a horizontal series of input steps in a sequence.
		/// </summary>
		private void DrawSequence(IEnumerable<Buttons> sequence, Vector2 position) {
			foreach (Buttons buttons in sequence) {
				DrawButtons(buttons, position);
				position.X += MeasureButtons(buttons).X;
			}
		}

		/// <summary>
		/// Calculates the size of what would be drawn by a call to DrawButtons.
		/// </summary>
		private Vector2 MeasureButtons(Buttons buttons) {
			Buttons direction = InputDirection.FromButtons(buttons);

			float width = 0;

			// If buttons has a direction,
			if (direction > 0) {
				var directionTexture = GetDirectionTexture(direction);
				if (directionTexture != null) {
					width = GetDirectionTexture(direction).Width;
					// If buttons has at least one non-directional button,
					if ((buttons & ~direction) > 0) {
						width += plusTexture.Width + padFaceTexture.Width;
					}
				}
			} else {
				width = padFaceTexture.Width;
			}

			return new Vector2(width, padFaceTexture.Height);
		}

		/// <summary>
		/// Draws the combined state of a set of buttons flags. The rendered output
		/// looks like a directional arrow, a group of buttons, or both concatenated
		/// with a plus sign operator.
		/// </summary>
		private void DrawButtons(Buttons buttons, Vector2 position) {
			// Get the texture to draw for the direction.
			Buttons direction = InputDirection.FromButtons(buttons);
			Texture2D directionTexture = GetDirectionTexture(direction);

			// If there is a direction, draw it.
			if (directionTexture != null) {
				spriteBatch.Draw(directionTexture, position, Color.White);
				position.X += directionTexture.Width;
			}

			// If any non-direction button is pressed,
			if ((buttons & ~direction) > 0) {
				// Draw a plus if both a direction and one more more buttons is pressed.
				if (directionTexture != null) {
					spriteBatch.Draw(plusTexture, position, Color.White);
					position.X += plusTexture.Width;
				}

				// Draw a gamepad with all inactive buttons in the background.
				spriteBatch.Draw(padFaceTexture, position, Color.White);

				// Draw each active button over the inactive game pad face.
				if ((buttons & Buttons.A) > 0) {
					spriteBatch.Draw(aButtonTexture, position, Color.White);
				}
				if ((buttons & Buttons.B) > 0) {
					spriteBatch.Draw(bButtonTexture, position, Color.White);
				}
				if ((buttons & Buttons.X) > 0) {
					spriteBatch.Draw(xButtonTexture, position, Color.White);
				}
				if ((buttons & Buttons.Y) > 0) {
					spriteBatch.Draw(yButtonTexture, position, Color.White);
				}
			}
		}

		/// <summary>
		/// Gets the texture for a given direction.
		/// </summary>
		private Texture2D GetDirectionTexture(Buttons direction) {
			switch (direction) {
				case InputDirection.Up:
					return upTexture;
				case InputDirection.Down:
					return downTexture;
				case InputDirection.Left:
					return leftTexture;
				case InputDirection.Right:
					return rightTexture;
				case InputDirection.TurnLeft:
					return turnLeftTexture;
				case InputDirection.TurnRight:
					return turnRightTexture;
				case InputDirection.UpLeft:
					return upLeftTexture;
				case InputDirection.UpRight:
					return upRightTexture;
				case InputDirection.DownLeft:
					return downLeftTexture;
				case InputDirection.DownRight:
					return downRightTexture;
				default:
					return null;
			}
		}
	}
}
